
package com.app.tablet.data.db;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.logging.Logger;

import com.app.tablet.data.dao.TabletDao;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class TabletDB extends SQLiteOpenHelper {

	private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;

	private static final String TagProject = "Tablet-Project";
	private static final String TagPage = "TabletDB-Page ::";

	// The Android's default system path of your application database.
	private static final String DB_PATH = "/data/data/com.app.tablet/databases/";

	private static final String DB_NAME = "tabletreminder.db";

	public static final int DB_VERSION = 1;

	private final Context myContext;

	private static TabletDB instance = null;

	private SQLiteDatabase sqLiteDataBase;

	private static int MAX_ATTEMPTS = 100;

	private static final int DELAY_BETWEEN_ATTEMPTS = 2000;

	public TabletDB(Context context) {
		super(context, DB_NAME, null, DB_VERSION);
		Log.i(TagProject, TagPage+"point 1");
		this.myContext = context;
		Log.i(TagProject, TagPage+"point ... 4");
		createDataBase();
	}

	/**
	 * Creates a empty database on the system and rewrites it with your own
	 * database.
	 * */
	public synchronized boolean createDataBase() {

		boolean dbExist = checkDataBase();
		if(dbExist){

			return true;
		} else{

			// Method from the parent class.
			getReadableDatabase();
			dbExist = copyDataBase();
			close();
		}

		return dbExist;
	}

	/**
	 * Check if the database already exist to avoid re-copying the file each
	 * time you open the application.
	 * 
	 * @return true if it exists, false if it doesn't
	 */
	private boolean checkDataBase() {

		SQLiteDatabase checkDB = null;
		try{
			String myPath = DB_PATH + DB_NAME;
			checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

		} catch(SQLiteException e){

		}
		if(checkDB != null){
			checkDB.close();
		}
		return checkDB != null ? true : false;
	}

	/**
	 * Copies your database from your local assets-folder to the just created
	 * empty database in the system folder, from where it can be accessed and
	 * handled. This is done by transferring byte stream.
	 * 
	 */
	private synchronized boolean copyDataBase() {

		try{
			// Open your local database as the input stream
			InputStream myInput = myContext.getAssets().open(DB_NAME);

			// Path to the just created empty database
			String outFileName = DB_PATH + DB_NAME;

			// Open the empty database as the output stream
			OutputStream myOutput = new FileOutputStream(outFileName);

			copy(myInput, myOutput);

			// Close the streams
			myOutput.flush();
			myOutput.close();
			myInput.close();

			return true;

		} catch(FileNotFoundException e){

		} catch(IOException e){

		}

		return false;

	}

	@Override
	public void onCreate(SQLiteDatabase db) {

	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
	}

	/**
	 * Provides the singleton instance of this class.
	 * 
	 * @param appContext
	 * @return
	 */
	public static synchronized TabletDB getInstance(Context appContext) {
		Log.i(TagProject, TagPage+"point 2");
		if(instance != null){
			Log.i(TagProject, TagPage+"not null ... 21");
			return instance;
		} else{
			Log.i(TagProject, TagPage+"null ... 21");
			instance = new TabletDB(appContext);
			Log.i(TagProject, TagPage+"null and after new ... 22");
			return instance;
		}
	}

	public static TabletDB getInstance() {
		Log.i(TagProject, TagPage+"point ... 3");
		return instance;
	}

	/**
	 * Method retrieves the SQLDatabase connection based on the mode being
	 * passed.Addresses the "database locked" related issue implemented in
	 * similar way as default Android Contact application.
	 * 
	 * @param mode
	 *            SQLiteDatabase.OPEN_READONLY & SQLiteDatabase.OPEN_READWRITE
	 * @return SQLiteDatabase object
	 */
	public SQLiteDatabase getConnection(int mode) {

		String strDBPath = DB_PATH + DB_NAME;
		for(int i = 0; i < MAX_ATTEMPTS; i++){
			try{
				if(mode == SQLiteDatabase.OPEN_READWRITE){
					return SQLiteDatabase.openDatabase(strDBPath, null, SQLiteDatabase.OPEN_READWRITE);
				} else{
					return SQLiteDatabase.openDatabase(strDBPath, null, SQLiteDatabase.OPEN_READONLY);
				}

			} catch(SQLiteException e){
				// We could get a "database locked" exception here, in which
				// case we should retry
				try{
					Thread.sleep(DELAY_BETWEEN_ATTEMPTS);
				} catch(InterruptedException ie){
				}
			}
		}
		return null;
	}

	/**
	 * 
	 * Copy bytes from an InputStream to an OutputStream.
	 * 
	 * This method buffers the input internally, so there is no need to use a
	 * BufferedInputStream.
	 * 
	 * Large streams (over 2GB) will return a bytes copied value of -1 after the
	 * copy has completed since the correct number of bytes cannot be returned
	 * as an int. For large streams use the copyLarge(InputStream, OutputStream)
	 * method.
	 * 
	 * @param input
	 *            the InputStream to read from
	 * @param output
	 *            the OutputStream to write to
	 * @return the number of bytes copied
	 * @throws IOException
	 *             if an I/O error occurs
	 */
	public static int copy(InputStream input, OutputStream output) throws IOException {
		long count = -1L;
		if(input != null){
			count = copyLarge(input, output);
			if(count > Integer.MAX_VALUE){
				return -1;
			}
		}
		return (int) count;
	}

	/**
	 * Copy bytes from a large (over 2GB) InputStream to an OutputStream.
	 * 
	 * This method buffers the input internally, so there is no need to use a
	 * BufferedInputStream.
	 * 
	 * @param input
	 *            the InputStream to read from
	 * @param output
	 *            the OutputStream to write to
	 * @return the number of bytes copied
	 * @throws IOException
	 *             if an I/O error occurs
	 */
	private static long copyLarge(InputStream input, OutputStream output) throws IOException {
		byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
		long count = 0;
		int n = 0;
		while(-1 != (n = input.read(buffer))){
			output.write(buffer, 0, n);
			count += n;
		}
		return count;
	}

}
